/*
 * FeedRepresentation.java
 *
 * Created on March 27, 2007, 11:55 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import org.atomojo.app.client.XMLRepresentationParser;
import org.atomojo.app.db.Feed;
import org.atomojo.app.db.Term;
import org.atomojo.app.db.TermInstance;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.WriterItemDestination;
import org.restlet.Application;
import org.restlet.data.CharacterSet;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.representation.OutputRepresentation;
import org.restlet.representation.Representation;

/**
 *
 * @author alex
 */
public class MetadataResource extends AtomResource {

   Reference myself;
   Reference feedRef;
   String resourceBase;
   Storage store;
   App app;
   
   /** Creates a new instance of FeedRepresentation */
   public MetadataResource(Application app,Feed feed,String resourceBase,Reference myself,Reference feedRef,Storage storage) {
      super(app,feed,storage);
      this.myself = myself;
      this.feedRef = feedRef;
      this.app = new App(getContext().getLogger(),feed.getDB(),storage,theApp.getMetadataService());
   }
   
   public Representation get() {
      //getContext().getLogger().info("feed: "+feed.getName()+", id: "+feed.getId()+", parent: "+feed.getParentId());
      return new OutputRepresentation(MediaType.APPLICATION_ATOM) {
         public void write(OutputStream os)
            throws IOException
         {
            setCharacterSet(CharacterSet.UTF_8);
            try {
               generate(storage,feed,myself,feedRef,resourceBase,new WriterItemDestination(new OutputStreamWriter(os,"UTF-8"),"UTF-8"));
            } catch (XMLException ex) {
               getContext().getLogger().log(Level.SEVERE,"Cannot serialize metadata feed.",ex);
               throw new IOException("Cannot serialize metadata feed: "+ex.getMessage());
            }
         }
      };
   }
   
   public String getTitle() 
      throws IOException
   {
      try {
         return storage.getFeedTitle(feed.getPath(),feed.getUUID());
      } catch (SQLException ex) {
         throw new IOException("Exception while getting feed path: "+ex.getMessage());
      }
   }
   
   public static void generate(Storage storage,Feed feed,Reference metaRef,Reference feedRef,String resourceBase,ItemDestination dest)
      throws XMLException
   {
      XMLRepresentationParser xmlParser = new XMLRepresentationParser();
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      dest.send(constructor.createDocument());
      dest.send(constructor.createElement(AtomResource.FEED_NAME));
      link(constructor,dest,"self",metaRef.toString());
      link(constructor,dest,"related",feedRef.toString());
      try {
         org.atomojo.app.client.Feed feedRep = new org.atomojo.app.client.Feed(xmlParser.load(storage.getFeedHead(feed.getPath(), feed.getUUID())));
         text(constructor,dest,AtomResource.TITLE_NAME,feedRep.getTitle());
         String summary = feedRep.getSummary();
         if (summary!=null) {
            text(constructor,dest,AtomResource.SUMMARY_NAME,summary);
         }
      } catch (Exception ex) {
         throw new XMLException("Exception while getting feed title.",ex);
      }
      text(constructor,dest,AtomResource.ID_NAME,feed.getUUID().toString());
      text(constructor,dest,AtomResource.UPDATED_NAME,AtomResource.toXSDDate(feed.getEdited()));
      try {
         Iterator<TermInstance<Feed>> categorization = feed.getTerms();
         while (categorization.hasNext()) {
            TermInstance<Feed> category = categorization.next();
            term(constructor,dest,category.getTerm().getURI(),category.getValue());

         }
      } catch (Exception ex) {
         throw new XMLException("Exception while getting categorization.",ex);
         
      }
      try {
         List<Feed> ancestors = new ArrayList<Feed>();
         Feed parent = feed;
         do {
            parent = parent.getParent();
            if (parent!=null) {
               ancestors.add(parent);
            }
         } while (parent!=null);
         Collections.reverse(ancestors);
         for (Feed ancestor : ancestors) {
            org.atomojo.app.client.Feed feedRep = new org.atomojo.app.client.Feed(xmlParser.load(storage.getFeedHead(ancestor.getPath(), ancestor.getUUID())));
            dest.send(constructor.createElement(AtomResource.ENTRY_NAME));
            text(constructor,dest,AtomResource.ID_NAME,ancestor.getUUID().toString());
            text(constructor,dest,AtomResource.PUBLISHED_NAME,AtomResource.toXSDDate(ancestor.getCreated()));
            text(constructor,dest,AtomResource.UPDATED_NAME,AtomResource.toXSDDate(ancestor.getEdited()));
            text(constructor,dest,AtomResource.TITLE_NAME,feedRep.getTitle());
            String summary = feedRep.getSummary();
            if (summary!=null) {
               text(constructor,dest,AtomResource.SUMMARY_NAME,summary);
            }
            term(constructor,dest,Categorization.ANCESTOR_TERM,null);
            Iterator<TermInstance<Feed>> categorization = ancestor.getTerms();
            while (categorization.hasNext()) {
               TermInstance<Feed> category = categorization.next();
               term(constructor,dest,category.getTerm().getURI(),category.getValue());

            }
            link(constructor,dest,"related",resourceBase+ancestor.getPath());
            dest.send(constructor.createElementEnd(AtomResource.ENTRY_NAME));
         }
      } catch (Exception ex) {
         throw new XMLException("Exception while getting ancestors.",ex);
      }
      try {
         Term hidden = feed.getDB().findTerm(Categorization.HIDDEN_TYPE_TERM);
         if (hidden==null) {
            hidden = feed.getDB().createTerm(Categorization.HIDDEN_TYPE_TERM);
         }
         Iterator<Feed> children = feed.getChildren();
         while (children.hasNext()) {
            Feed child = children.next();
            if (child.getTerm(hidden)!=null) {
               continue;
            }
            org.atomojo.app.client.Feed feedRep = new org.atomojo.app.client.Feed(xmlParser.load(storage.getFeedHead(child.getPath(), child.getUUID())));
            dest.send(constructor.createElement(AtomResource.ENTRY_NAME));
            text(constructor,dest,AtomResource.ID_NAME,child.getUUID().toString());
            text(constructor,dest,AtomResource.PUBLISHED_NAME,AtomResource.toXSDDate(child.getCreated()));
            text(constructor,dest,AtomResource.UPDATED_NAME,AtomResource.toXSDDate(child.getEdited()));
            text(constructor,dest,AtomResource.TITLE_NAME,feedRep.getTitle());
            String summary = feedRep.getSummary();
            if (summary!=null) {
               text(constructor,dest,AtomResource.SUMMARY_NAME,summary);
            }
            term(constructor,dest,Categorization.CHILD_TERM,null);
            Iterator<TermInstance<Feed>> categorization = child.getTerms();
            while (categorization.hasNext()) {
               TermInstance<Feed> category = categorization.next();
               term(constructor,dest,category.getTerm().getURI(),category.getValue());

            }
            link(constructor,dest,"related",feedRef.toString()+child.getName()+"/");
            dest.send(constructor.createElementEnd(AtomResource.ENTRY_NAME));
         }
         
      } catch (Exception ex) {
         throw new XMLException("Exception while getting feed children.",ex);
      }
      dest.send(constructor.createElementEnd(AtomResource.FEED_NAME));
      dest.send(constructor.createDocumentEnd());
   }
   
   static void text(ItemConstructor constructor,ItemDestination dest,Name name,String text)
      throws XMLException
   {
      dest.send(constructor.createElement(name));
      if (text!=null) {
         dest.send(constructor.createCharacters(text));
      }
      dest.send(constructor.createElementEnd(name));
   }
      
   static void term(ItemConstructor constructor,ItemDestination dest,URI term,Object value)
      throws XMLException
   {
      String [] parts = new String[2];
      Term.split(term,parts);
      try {
         Element termE = constructor.createElement(AtomResource.CATEGORY_NAME);
         if (!parts[0].equals("http://www.atomojo.org/O/keyword/")) {
            termE.setAttributeValue("scheme",parts[0]);
         }
         termE.setAttributeValue("term",URLDecoder.decode(parts[1],"UTF-8"));
         dest.send(termE);
         if (value!=null && !(value instanceof Nil)) {
            dest.send(constructor.createCharacters(value.toString()));
         }

         dest.send(constructor.createElementEnd(AtomResource.CATEGORY_NAME));
      } catch (UnsupportedEncodingException ex) {
         throw new XMLException("Cannot decode term "+parts[1],ex);
      }
   }
   static void link(ItemConstructor constructor,ItemDestination dest,String rel,String href)
      throws XMLException
   {
      Element linkE = constructor.createElement(AtomResource.LINK_NAME);
      linkE.setAttributeValue("rel",rel);
      linkE.setAttributeValue("href",href);
      linkE.setAttributeValue("type","application/atom+xml");
      dest.send(linkE);
      
      dest.send(constructor.createElementEnd(AtomResource.LINK_NAME));
   }
}
